home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / UTILITY / CMDED2E6.ARJ / STRSTACK.ASM < prev    next >
Assembly Source File  |  1992-06-30  |  28KB  |  1,042 lines

  1. ; STRSTACK.ASM
  2. ; (c) 1989, 1990 Ashok P. Nadkarni
  3. ;
  4. ; Functions to implement the string stack object for CMDEDIT. Small Model only.
  5. ; strstk_output added by wd
  6.  
  7.     INCLUDE common.inc
  8.     INCLUDE general.inc
  9. STRSTACK_ASM    EQU    1
  10.  
  11.  
  12. CSEG    SEGMENT PARA PUBLIC 'CODE'
  13. CSEG    ENDS
  14.  
  15. DGROUP    GROUP    CSEG
  16.  
  17.     INCLUDE    buffers.inc
  18.  
  19. CSEG    SEGMENT PARA PUBLIC 'CODE'
  20.  
  21.     ASSUME    CS:DGROUP, DS:DGROUP, SS:DGROUP, ES:DGROUP
  22.  
  23.     EXTRN    stre_cmp:PROC
  24.     EXTRN    output_newline:PROC        ;added by wd
  25.     EXTRN    output_counted_string:PROC    ;added by wd
  26.  
  27. ;+
  28. ; FUNCTION : strstk_init
  29. ;
  30. ;    Initializes a $STRING_STACK descriptor. Note that buffer will contain
  31. ;    overhead as well as the actual data itself.
  32. ;
  33. ; Parameters:
  34. ;    BX := address of descriptor
  35. ;    AX := address of buffer
  36. ;    CX := buffer size
  37. ;
  38. ; Returns:
  39. ;    AX = 0 if success
  40. ;    else -1
  41. ;
  42. ; Register CX destroyed.
  43. ;-
  44.  
  45. strstk_init proc near
  46.  
  47.     cmp    cx,2        ;Need at least 2 bytes in buffer (sentinel)
  48.     jb    @strstk_init_90    ;
  49.     cmp    cx,32767    ; and LESS than 32767 (Need to represent
  50.     jnc    @strstk_init_90 ; + and - differences between first location
  51.                 ; and first location beyond buffer in 16 bits)
  52.     mov    [bx].low_end,ax    ;starting address
  53.     add    cx,ax        ;Location after last addressable byte of buffer
  54.                 ;Various functions assume byte beyond last
  55.                 ;buffer location is addressable without
  56.                 ;segment wraparound.
  57.     jc    @strstk_init_90    ;Segment wraparound.
  58.     dec    cx        ;CX->Last byte in buffer
  59.     mov    [bx].high_end,cx
  60.     call    near ptr strstk_reset
  61.     xor    ax,ax        ;success
  62.     jmp    short @strstk_init_99
  63. @strstk_init_90:         ;Buffer too small or 
  64.     mov    ax,-1         ; overflow (add ax,cx instruction)
  65. @strstk_init_99:
  66.     ret
  67.  
  68. strstk_init endp
  69.  
  70.  
  71.  
  72. ;+
  73. ; FUNCTION : strstk_reset
  74. ;
  75. ;    Resets a specified stack to its initial state. The sentinel at the
  76. ;    bottom of the stack is assumed to be already there.
  77. ;
  78. ; Parameters :
  79. ;    BX := address of buffer descriptor
  80. ;
  81. ; Returns :
  82. ;    Nothing.
  83. ;
  84. ; Registers destroyed:
  85. ;    AX
  86. ;-
  87. strstk_reset proc near
  88.     mov    ax,[bx].low_end
  89.     xchg    ax,bx
  90.     mov    word ptr [bx],0    ;header & trailer for sentinel null string
  91.     xchg    ax,bx
  92.     inc    ax        ;
  93.     mov    [bx].top,ax    ;Stack top (sentinel null)
  94.     mov    [bx].cur,ax    ;Current string (sentinel null)
  95.     mov    [bx].savecur,ax
  96.     mov    ax,[bx].high_end        ;AX->last byte of buffer
  97.     inc    ax                ;AX->first byte beyond buffer
  98.     mov    [bx].topmark,ax            ;No markers
  99.     ret
  100. strstk_reset endp
  101.  
  102.  
  103. ;+
  104. ; FUNCTION: strstk_save_cur
  105. ;
  106. ;    This function saves the cur pointer in the cursave field of the
  107. ;    descriptor structure. It can then be restored with the
  108. ;    strstk_restore_cur call. It is the caller's responsibility to
  109. ;    ensure that the stack does not change in the meanwhile in such a
  110. ;    way as to invalidate the pointer into it. Generally do not do
  111. ;    anything except move the cur pointer around to access different
  112. ;    strings.
  113. ;
  114. ; Parameters :
  115. ;    BX := address of buffer descriptor
  116. ;
  117. ; Returns :
  118. ;    Nothing.
  119. ;
  120. ; Registers destroyed:
  121. ;    AX
  122. strstk_save_cur proc near
  123.     mov    ax,[bx].cur
  124.     mov    [bx].savecur,ax
  125.     ret
  126. strstk_save_cur endp
  127.  
  128. ;+
  129. ; FUNCTION: strstk_restore_cur
  130. ;
  131. ;    This function restores the cur pointer from the cursave field of the
  132. ;    descriptor structure where it was stored through the
  133. ;    strstk_save_cur call. It is the caller's responsibility to
  134. ;    ensure that the stack does not change in the meanwhile in such a
  135. ;    way as to invalidate the pointer into it. Generally do not do
  136. ;    anything except move the cur pointer around to access different
  137. ;    strings. Also, make sure you do a strstk_save_cur before every
  138. ;    strstk_restore_cur.
  139. ;
  140. ; Parameters :
  141. ;    BX := address of buffer descriptor
  142. ;
  143. ; Returns :
  144. ;    Nothing.
  145. ;
  146. ; Registers destroyed:
  147. ;    AX
  148. strstk_restore_cur proc near
  149.     mov    ax,[bx].savecur
  150.     mov    [bx].cur,ax
  151.     ret
  152. strstk_restore_cur endp
  153.  
  154.  
  155.  
  156. ;+
  157. ; FUNCTION : strstk_space
  158. ;
  159. ;    Returns the available space in the buffer. This is 2 less than the
  160. ;    actual number of bytes remaining since we need space for a header
  161. ;    and a trailer for at least one string.
  162. ;
  163. ; Parameters:
  164. ;    BX := address of buffer descriptor
  165. ;
  166. ; Returns:
  167. ;    CF :=    1 if no space in stack even for a header or trailer
  168. ;        0 otherwise
  169. ;    AX :=    Available space (length of max string that can be fitted)
  170. ;        if CF = 0, if CF = 1, AX is indeterminate
  171. ;-
  172. strstk_space proc near
  173.     mov    ax,[bx].topmark    ;Points BEYOND last usable byte
  174.     sub    ax,[bx].top    ;actual space in buffer + 1
  175.                 ;Need to deduct 1+2(for header/trailer)
  176.     sub    ax,3        ;Don't use multiple 'dec ax' here
  177.                 ; since it does not set CF.
  178.     ret
  179. strstk_space endp
  180.  
  181.  
  182.  
  183.  
  184. ;+
  185. ; FUNCTION : strstk_settop
  186. ;
  187. ;    Sets the current string pointer to point to the newest (top
  188. ;    of stack) string.
  189. ;
  190. ; Parameters:
  191. ;    BX :=    address of buffer descriptor 
  192. ;
  193. ; Returns:
  194. ;    Nothing.
  195. ;
  196. ; Register AX destroyed.
  197. ;-
  198. strstk_settop proc near
  199.     mov    ax,[bx].top
  200.     mov    [bx].cur,ax
  201.     ret
  202. strstk_settop endp
  203.  
  204.  
  205.  
  206. ;+
  207. ; FUNCTION : strstk_setbot
  208. ;
  209. ;    Sets the current string pointer to point to the oldest (bottom
  210. ;    of stack) string. If the stack is not empty, the null string at
  211. ;    the bottom is ignored.
  212. ;
  213. ; Parameters:
  214. ;    BX :=    address of buffer descriptor 
  215. ;
  216. ; Returns:
  217. ;    Nothing.
  218. ;
  219. ; Registers AX,DX destroyed.
  220. ;-
  221. strstk_setbot proc near
  222.     mov    ax,[bx].low_end
  223.     inc    ax
  224.     cmp    ax,[bx].top    ;Is stack empty ?
  225.     je    @strstk_setbot_99 ;Yes
  226.     inc    ax        ;Point ax to header of oldest string
  227.     xchg    ax,si        ;Store in SI
  228.     mov    dl,[si]        ;get length of string
  229.     xor    dh,dh
  230.     add    si,dx        ;Point SI to last byte in string
  231.     inc    si        ;Point SI at trailer
  232.     xchg    ax,si        ;Restore SI and set AX to point to trailer
  233. @strstk_setbot_99:
  234.     mov    [bx].cur,ax    ;Set current pointer
  235.     ret
  236. strstk_setbot endp
  237.  
  238.  
  239.  
  240. ;+
  241. ; FUNCTION : strstk_kill
  242. ;
  243. ;    Deletes the "current" string from the stack. All strings above it
  244. ;    are moved up. "current" string is updated to point to the string
  245. ;    above the deleted string unless the topmost string was deleted
  246. ;    in which case it is set to top of stack. 
  247. ;
  248. ;    Any markers pointing at the deleted string are updated to
  249. ;    point to the new "current" string. Any markers pointing above
  250. ;    the deleted string updated to keep pointing to their
  251. ;    respective strings even after the latter are moved down.
  252. ;    Naturally, the marks below the deleted string do not change.
  253. ;
  254. ; Parameters:
  255. ;    BX :=    address of buffer descriptor
  256. ;
  257. ; Returns:
  258. ;    Nothing
  259. ;
  260. ; Registers AX,CX,DX destroyed
  261. ;-
  262. strstk_kill proc near
  263.     @save    si,di
  264.  
  265.     mov    si,[bx].cur
  266.     mov    ax,[bx].low_end    ;When current points to the sentinel string,
  267.     inc    ax        ; exit without deleting it
  268.     cmp    ax,si        ;Sentinel current ?
  269.     je    @strstk_kill_99 ;Yes, exit
  270.  
  271.     mov    cx,[bx].top    ;topmost occupied location
  272.     mov    dx,cx        ;remember it
  273.     sub    cx,si        ;Num bytes to be moved into vacated positions
  274.  
  275.     xor    ax,ax
  276.     mov    di,si        ;di will be used to point to the header of
  277.                 ; the deleted string.
  278.     lodsb            ;Get the string length into AX.
  279.                 ;At the same time SI now points to header
  280.                 ;of the string following the condemned string.
  281.     inc    ax
  282.     sub    di,ax        ;di now points to the condemned string header.
  283.     inc    ax
  284.     push    ax        ;Remember how many bytes are to be removed
  285.     sub    dx,ax        ;Top of stack is now (length of string + 2)
  286.     mov    [bx].top,dx    ; below the original top
  287.  
  288.     jcxz    @strstk_kill_50 ;If # bytes to be moved is 0, then the
  289.                 ;deleted string was at the top of the stack.
  290.     push    di        ;remember header position
  291.     rep    movsb        ;Copy CX bytes down into vacated positions
  292.     pop    si        ;Header position of new "current" string
  293.                 ; (same location as header of old current)
  294.     lodsb            ;SI points to first byte of string
  295.     xor    ah,ah        ; and AL = length of new current string.
  296.     add    ax,si        ;AX now points to trailer of new "current"
  297.     jmp    short @strstk_kill_90 ;exit
  298.  
  299. @strstk_kill_50:        ;Deleted element was top of stack
  300.     xchg    ax,dx        ;ax = top, we want cur to be == top
  301.  
  302. @strstk_kill_90:
  303.     xchg    ax,[bx].cur    ;cur = top, ax = old current
  304.     pop    cx        ;Restore the removed byte count
  305.                 ; (counterpart of the 'push ax' above)
  306.     IF    WANT_MARKERS
  307.     call    strstk_update_marks ;Update marks for strings that were moved
  308.                     ;  ax == old cur, cx == displacement
  309.     ENDIF
  310. @strstk_kill_99:
  311.     @restore
  312.     ret
  313. strstk_kill endp
  314.  
  315.  
  316.  
  317.  
  318. ;+
  319. ; FUNCTION : strstk_push
  320. ;
  321. ;    Pushes a string onto the top of the stack. If the force flag parameter
  322. ;    is set to any value other than 0, one or more strings at the bottom
  323. ;    of the stack are deleted to make room fir the new string. If the force
  324. ;    flag is 0, then an error is returned if there is not sufficient room
  325. ;    in the stack. An error is also returned if the string is bigger than
  326. ;    stack size. The stack is left unaltered for both error conditions.
  327. ;
  328. ; Parameters:
  329. ;    BX :=    address of buffer stack descriptor
  330. ;    AL :=    Length of string
  331. ;    CX :=    force flag
  332. ;    DX :=    address of string to be pushed
  333. ;
  334. ; Returns:
  335. ;    Carry flag is set if error (not enough stack space), else it is clear.
  336. ;
  337. ; Registers AX,CX,DX destroyed.
  338. ;-
  339. strstk_push proc near
  340.     @save    si,di
  341.     xor    ah,ah        ;Clear high byte of length.
  342.     mov    si,dx        ;SI := source string
  343.     push    ax        ;save length
  344.     jcxz    @strstk_push_10    ;Jump if force flag is 0
  345.     call    strstk_makespace ;Make sure enough space, else make space
  346.     pop    cx        ;restore length
  347.     jc    @strstk_push_99    ;Error return by strstk_makespace
  348.     jmp    short @strstk_push_20 ;Everything OK, go push string
  349. @strstk_push_10:
  350.     call    near ptr strstk_space    ;Find out how much space is left
  351.     pop    cx        ;Restore string length
  352.     jb    @strstk_push_99        ;Not even enough for header/trailer,
  353. ;                     error  return
  354.     cmp    ax,cx        ;Enough space on stack ?
  355.     jb    @strstk_push_99    ;Nope, error return
  356. @strstk_push_20:        ;OK, copy string onto stack
  357.     mov    di,[bx].top    ;DI := last occupied location on stack
  358.     inc    di        ;DI := address of header for new string
  359.     mov    ax,cx        ;Get length into AL
  360.     stosb            ;Store length in header
  361.      rep    movsb        ;Copy string
  362.     mov    [bx].top,di    ;New top of stack is trailer of topmost string
  363.     mov    [bx].cur,di    ;Ditto for current string
  364.     stosb            ;Store length in trailer
  365.     clc            ;Success return
  366. @strstk_push_99:
  367.     @restore
  368.     ret
  369. strstk_push endp
  370.  
  371.  
  372. ;+
  373. ; FUNCTION : strstk_fwd_match
  374. ;
  375. ;    Searches towards the top of the stack, starting from the string
  376. ;    above the current string looking for a string that has the specified
  377. ;    pattern as a prefix.  If the pattern length is 0, then the match is
  378. ;    universal and the new current string is simply the one immediately
  379. ;    above the current one.  The function can thus be used to move the
  380. ;    cur pointer up the stack one string at a time. If the current
  381. ;    string is at the top of the stack, the cur pointer remains
  382. ;    unchanged.
  383. ;
  384. ; Parameters:
  385. ;    BX :=    address of buffer stack descriptor
  386. ;    AX :=    Address of pattern
  387. ;    CX :=    Length of pattern 
  388. ; Returns:
  389. ;    AX  = length of matched string (added by wd)
  390. ;    CF  = 1 if no match or if at top of stack
  391. ;        = 0 if success
  392. ;
  393. ; Registers CX destroyed.
  394. ;-
  395. strstk_fwd_match proc near
  396.     @save    si,di
  397.     mov    si,[bx].cur    ;SI:=current ptr
  398.     mov    di,AX        ;Pattern address
  399.     
  400. @strstk_fwd_match_10:
  401.     cmp    si,[bx].top    ;Are we at top of stack ?
  402.     je    @strstk_fwd_match_90 ;Yes, error exit
  403.     inc    si        ;SI:=point to next header
  404.     lodsb
  405.     xor    ah,ah        ;AX := string length
  406.     cmp    ax,cx        ;Is the pattern longer than string ?
  407.     jnb    @strstk_fwd_match_70 ;If not go try a match
  408.                 ;Yes, then try next string
  409.     add    si,ax        ;Point to trailer
  410.     jmp    short @strstk_fwd_match_10 ;and loop back    
  411. @strstk_fwd_match_70:        ;OK, see if pattern is the string prefix
  412.     push    cx        ;Remember pattern length
  413.     push    ax        ;Remember string length
  414.     call    near ptr stre_cmp ;Check if pattern is a prefix
  415.     pop    cx        ;Put string length into cx (wd tweaked this bit)
  416.     lahf            ;Save value returned by stre_cmp
  417.     add    si,cx        ;SI := trailer
  418.     sahf            ;Restore stre_cmp result
  419.     mov    ax,cx        ;Restore string length in ax
  420.     pop    cx        ;Restore pattern length
  421.     jne    @strstk_fwd_match_10 ;No match, loop back
  422.     clc            ;No errors
  423.     jmp    short @strstk_fwd_match_99
  424.  
  425. @strstk_fwd_match_90:
  426.     stc            ;Error return
  427. @strstk_fwd_match_99:
  428.     mov    [bx].cur,si
  429.     @restore
  430.     ret
  431. strstk_fwd_match endp
  432.  
  433.  
  434. ;+
  435. ; FUNCTION : strstk_bck_match
  436. ;
  437. ;    Searches towards the bottom of the stack, starting from the string
  438. ;    below the current string looking for a string that has the specified
  439. ;    pattern as a prefix.  If the pattern length is 0, then the match is
  440. ;    universal and the new current string is simply the one immediately
  441. ;    below the current one.  The function can thus be used to move the
  442. ;    cur pointer down the stack one string at a time. If the current
  443. ;    string is at the bottom of the stack, the cur pointer remains
  444. ;    unchanged.
  445. ;
  446. ; Parameters:
  447. ;    BX :=    address of buffer stack descriptor
  448. ;    AX :=    Address of pattern
  449. ;    CX :=    Length of pattern 
  450. ; Returns:
  451. ;    AX  = length of matched string (added by wd)
  452. ;    CF  = 1 if no match or if at bottom of stack
  453. ;        = 0 if success
  454. ;
  455. ; Registers CX destroyed.
  456. ;-
  457. strstk_bck_match proc near
  458.     @save    si,di
  459.     push    bp
  460.     mov    bp,sp
  461.     sub    sp,2
  462. sentinel EQU <word ptr [bp-2]>
  463.     push    ax        ;remember pattern address
  464.     mov    di,[bx].low_end    ;Buffer bottom
  465.     inc    di
  466.     mov    si,[bx].cur    ;SI:=current pointer
  467.     cmp    si,di        ;At stack bottom ? ( low_end + 1 == cur)
  468.     je    @strstk_bck_match_90 ;If so exit,
  469.                 ; (the 'push AX' is cleaned up by unlink)
  470.     mov    sentinel,di    ;remember sentinel value
  471.  
  472.     pop    di        ;Restore pattern address
  473.  
  474. ; Prime for loop below
  475.     xor    ah,ah
  476.     mov    al,[si]        ;AX<-length of current string
  477.     sub    si,ax        ;SI->start of string
  478.  
  479. @strstk_bck_match_9:
  480. ; Loop begin. SI points to the first byte of string last compared. This
  481. ; cannot be the sentinel string.
  482.     dec    si        ;SI->header of current string
  483.     dec    si        ;SI->trailer of previous string
  484.  
  485. @strstk_bck_match_10:
  486. ; At this point SI points to the trailer of string to try'n'match
  487.     cmp    si,sentinel    ;Are we at bottom ?
  488.     je    @strstk_bck_match_90 ;Yes, exit
  489.     mov    al,[si]        ;String length
  490.     xor    ah,ah
  491.     sub    si,ax        ;Point to first byte of string
  492.     cmp    ax,cx        ;Is the pattern longer than string ?
  493.     jb    @strstk_bck_match_9 ;Yes, then try next string
  494.                 ;OK, try see if pattern is the string prefix
  495.     push    cx        ;Remember pattern length
  496.     call    near ptr stre_cmp ;Check if pattern is a prefix
  497.     pop    cx
  498.     jne    @strstk_bck_match_9 ;Not a prefix, go try next one
  499.     xor    ax,ax
  500.     mov    al,-1[si]    ;AX<-length of matched string
  501.     add    si,ax        ;Point SI to trailer of matched string
  502. ;     Successful match, return with CF clear.
  503. ;    Carry flag is guaranteed clear since no o'flow is possible in
  504. ;    the add si,ax instruction. Hence comment out the following clc.
  505. ;    clc
  506.     jmp    short @strstk_bck_match_99
  507. @strstk_bck_match_90:
  508.     stc            ;Error return
  509.  
  510. @strstk_bck_match_99:
  511.     mov    [bx].cur,si    ;Update current string pointer
  512.     mov    sp,bp
  513.     pop    bp
  514.     @restore
  515.     ret
  516. strstk_bck_match endp
  517.  
  518.  
  519.  
  520.  
  521. ;+
  522. ; FUNCTION : strstk_fwd_find,strstk_bck_find
  523. ;
  524. ;    Searches towards the top/bottom of the stack, starting from the string
  525. ;    above/below the current string looking for a string that is the same
  526. ;    as the specified one. If the current string is at the top/bottom
  527. ;    the stack, the cur pointer remains unchanged.
  528. ;
  529. ; Parameters:
  530. ;    BX :=    address of buffer stack descriptor
  531. ;    AX :=    Address of string
  532. ;    CX :=    Length of string
  533. ; Returns:
  534. ;    CF  = 1 if no match or if at top/bottom of stack
  535. ;        = 0 if success
  536. ;
  537. ; Registers AX,CX,DX destroyed.
  538. ;-
  539. strstk_find proc near
  540. strstk_fwd_find LABEL near
  541.     push    di            ;Save di
  542.     push    si            ;and si
  543.     mov    di,offset CSEG:strstk_fwd_match
  544.     jmp    short @strstk_find_8
  545. strstk_bck_find LABEL near
  546.     push    di            ;Save di
  547.     push    si            ;and si
  548.     mov    di,offset CSEG:strstk_bck_match
  549. @strstk_find_8:
  550.     mov    si,ax            ;Save string address in SI
  551. @strstk_find_10:
  552. ; Loop start
  553.     mov    ax,si            ;AX->string
  554.     push    cx            ;Save string length
  555.     call    di             ;Look for prefix
  556.     pop    cx            ;Restore string length
  557.     jc    @strstk_find_99        ;Not found
  558.     push    cx
  559.     xor    cx,cx            ;Don't want to copy, just need size
  560.     call    near ptr strstk_copy    ;AX<-length of current string
  561.     pop    cx            ;Restore passed length
  562.     cmp    ax,cx            ;Strings match ?
  563.     jne    @strstk_find_10        ;Lengths not same, keep looking
  564.     clc                ;Strings same
  565. @strstk_find_99:
  566.     pop    si
  567.     pop    di
  568.     ret
  569. strstk_find endp
  570.  
  571.  
  572.  
  573. ;+
  574. ; FUNCTION : strstk_makespace
  575. ;
  576. ;    Deletes enough strings from the bottom of the stack to make room
  577. ;    for a string of the length specified in AX. If the stack size is
  578. ;    smaller than the requested size, the stack is left unchanged and
  579. ;    an error indication returned. If there is already enough room in
  580. ;    the stack, the stack is left unchanged. In both cases the current
  581. ;    string ptr is updated to point to the top of the stack. Note that
  582. ;    any marks, if present, reduce the space that is available. 
  583. ;
  584. ;    The routine is not very efficient since it keeps calling strstk_kill
  585. ;    rather than deleting the required number in one shot. But it is
  586. ;    more compact.
  587. ; Parameters:
  588. ;    BX :=    pointer to buffer descriptor
  589. ;    AX :=    requested length
  590. ; Returns:
  591. ;    CF    = 1 if error (requested length to large for stack)
  592. ;        = 0 success
  593. ;     Also changes current string pointer to point to top of stack.
  594. ; Registers AX,CX,DX destroyed.
  595. ;-
  596. strstk_makespace proc near
  597.     @save    si,di
  598.     xchg    ax,si        ;Save requested length in SI
  599.     call    strstk_size    ;AX := size of largest string that can
  600.                 ; fit in an empty stack
  601.     cmp    ax,si        ;Smaller than requested length ?
  602.     jb    @strstk_makespace_99 ;Yes, error exit
  603.     call    strstk_setbot    ;Set current string to bottommost
  604.  
  605. ;Now keep iterating until enough strings have been deleted. Since we 
  606. ;have already checked that the stack is large enough, the loop below
  607. ;is guaranteed to terminate.
  608.  
  609. @strstk_makespace_10:
  610. ; Note that the strstk_space routine returns a 0 if there are less than
  611. ; the 2 bytes required for header/trailer. The carry flag must be
  612. ; checked to distinguish this from the situation where there is room
  613. ; for a null string (exactly 2 bytes available)
  614.     call    near ptr strstk_space    ;AX := available space
  615.     jnc    @strstk_makespace_70    ;Jump if > 2 bytes available
  616. @strstk_makespace_65:
  617.     call    strstk_kill        ;If not delete one more
  618.     jmp    short @strstk_makespace_10 ;And keep trying
  619. @strstk_makespace_70:
  620.     cmp    ax,si            ;Enough space available ?
  621.     jb    @strstk_makespace_65    ;Yes, exit
  622. @strstk_makespace_99:
  623.     call    near ptr strstk_settop
  624.     @restore
  625.     ret
  626. strstk_makespace endp
  627.  
  628.  
  629. ;+
  630. ; FUNCTION : strstk_copy
  631. ;
  632. ;    Returns a copy of the current stack entry. Stack is unchanged.
  633. ;    If the stack is empty, a 0 length string is returned.
  634. ;    If the user buffer is not large enough, as many characters as
  635. ;    possible are copied into it. AX reflects the length of the actual
  636. ;    string and not just the copied part and the carry flag is set.
  637. ;    Thus this routine can also be used to find the length of the current
  638. ;    string by passing it a zero length buffer.
  639. ;
  640. ; Parameters:
  641. ;    BX    := pointer to buffer descriptor
  642. ;    AX    := starting address of location into which the string
  643. ;            is to be copied
  644. ;    CX    := size of buffer
  645. ;
  646. ; Returns:
  647. ;    AX    = length of current string (ACTUAL LENGTH, NOT COPIED LENGTH)
  648. ;    CF    = 0 if the user buffer is large enough
  649. ;        = 1 if user buffer too small
  650. ;          As many chars as possible are returned in the use buffer.
  651. ; Register CX destroyed.
  652. ;-
  653. strstk_copy proc near
  654.     @save    si,di
  655.     mov    di,ax        ;Destination buffer
  656.     mov    si,[bx].cur    ;SI := trailer of current string
  657.     mov    al,[si]
  658.     xor    ah,ah        ;AX:=length of current string
  659.     sub    si,ax        ;SI->first byte of string
  660.     cmp    cx,ax        ;User buffer large enough ?
  661.     jb    @strstk_copy_5    ;No, error return
  662.                 ;Note empty stack case automatically handled.
  663.     mov    cx,ax        ;CX<-length of string to copy
  664. @strstk_copy_5:
  665.     pushf            ;Save carry flags
  666. ;    CX == number of bytes to copy
  667.     rep    movsb
  668.     popf            ;Restore CF
  669.     @restore
  670. ;    Return AX = length of actual string
  671.     ret
  672. strstk_copy endp
  673.  
  674.  
  675.  
  676. ;+
  677. ; FUNCTION : strstk_size
  678. ;
  679. ;    Returns the maximum size string that can fit in the buffer if all
  680. ;    strings are deleted (but not markers).
  681. ;
  682. ; Parameters:
  683. ;    BX    := buffer descriptor address
  684. ;
  685. ; Returns:
  686. ;    AX    := Max string size for buffer if all strings are deleted
  687. ;
  688. ; All registers (except AX)  are preserved.
  689. ;-
  690. strstk_size proc near
  691.     mov    ax,[bx].topmark        ;Address beyond last available
  692.                     ; for strings
  693.     sub    ax,[bx].low_end        ;bottom of buffer (sentinel header)
  694.     sub    ax,4            ;Need 2 for header trailer + 2 for
  695.                     ; sentinel
  696.     jnb    @strstk_size_99        ;
  697.     xor    ax,ax            ;No space !
  698. @strstk_size_99:
  699.     ret
  700. strstk_size endp
  701.  
  702.  
  703.  
  704.  
  705.  
  706. ;+
  707. ; FUNCTION : strstk_prefix
  708. ;
  709. ;    Compares the passed string to check if it is a prefix of the
  710. ;    current string in the stack.
  711. ;
  712. ; Parameters:
  713. ;    BX    := buffer descriptor address
  714. ;    AX    := address of string
  715. ;    CX    := length of string
  716. ;
  717. ; Returns:
  718. ;    ZF    = 1 if string is prefix
  719. ;          0 if not
  720. ;
  721. ; Register(s) destroyed:
  722. ;-
  723. strstk_prefix    proc near
  724.     @save    si,di
  725.     xchg    di,ax            ;DI->string
  726.     mov    si,[bx].cur        ;SI->trailer byte of stack element
  727.     xor    ah,ah            ;Clear high byte
  728.     mov    al,[si]            ;AX<-length of string
  729.     cmp    ax,cx            ;Passed string longer ?
  730.     jb    @strstk_prefix_90    ;No, return `not prefix'
  731.     sub    si,ax            ;SI->start of string
  732.     call    near ptr stre_cmp
  733. ;    ZF is set if match occurs, 0 otherwise
  734.     jmp    short @strstk_prefix_99
  735. @strstk_prefix_90:
  736.     inc    al            ;Set ZF=0 (no match)
  737. @strstk_prefix_99:
  738.     @restore
  739.     ret
  740. strstk_prefix endp
  741.  
  742.  
  743.  
  744.  
  745. ;+
  746. ; FUNCTION : strstk_compare
  747. ;
  748. ;    Compares the passed string against the current string in the stack.
  749. ;
  750. ; Parameters:
  751. ;    BX    := buffer descriptor address
  752. ;    AX    := address of string
  753. ;    CX    := length of string
  754. ;
  755. ; Returns:
  756. ;    ZF    = 1 if strings equal
  757. ;          0 if not equal.
  758. ;
  759. ; Register(s) destroyed:
  760. ;-
  761. strstk_compare    proc near
  762.     @save    si,di
  763.     xchg    di,ax            ;DI->string
  764.     mov    si,[bx].cur        ;SI->trailer byte of stack element
  765.     xor    ah,ah            ;Clear high byte
  766.     mov    al,[si]            ;AX<-length of string
  767.     cmp    ax,cx            ;Same length?
  768.     jne    @strstk_compare_99    ;No, return `not equal'
  769.     jcxz    @strstk_compare_99    ;Equal (zero length), ZF
  770. ;                     already set
  771.     sub    si,ax            ;SI->start of string
  772.     call    near ptr stre_cmp    ;Case-insensitive compare
  773. ;    ZF is set if match occurs, 0 otherwise
  774. @strstk_compare_99:
  775.     @restore
  776.     ret
  777. strstk_compare endp
  778.  
  779.  
  780.  
  781.     IF    WANT_MARKERS
  782. ;+
  783. ; FUNCTION : strstk_update_markers
  784. ;
  785. ;    This routine is called to update the markers that point to various
  786. ;    strings in the stack. This is necessary when strings are deleted
  787. ;    causing the string positions to change.
  788. ;
  789. ; Parameters:
  790. ;    BX    := address of stack descriptor
  791. ;    AX    := The address of the old (deleted) current string
  792. ;    CX    := Number of bytes by which the strings were displaced.
  793. ;
  794. ; Returns:
  795. ;    Nothing.
  796. ; Registers AX is destroyed.
  797. ;-
  798. strstk_update_marks proc near
  799.     @save    si,di
  800.     xchg    ax,si        ;Store old current address in SI
  801.     mov    di,[bx].topmark    ;DI will iterate through the marks
  802. @strstk_update_marks_20:
  803.     cmp    di,[bx].high_end
  804.     jnc    @strstk_update_marks_99 ;All done
  805.     mov    ax,[di]        ;Old address of marked string
  806.     cmp    si,ax        ;Compare with address of deleted string
  807.     jne    @strstk_update_marks_40    ;
  808.     mov    ax,[bx].cur    ;Mark pointed to deleted string so
  809.     jmp    short @strstk_update_marks_50    ;point it to new current
  810. @strstk_update_marks_40:
  811.     jg    @strstk_update_marks_50    ;If mark pointed BELOW, no change
  812.     sub    ax,cx        ;Else subtract the displacement
  813. @strstk_update_marks_50:
  814.     stosw            ;Store new value back, increment DI to point
  815.     jmp    short @strstk_update_marks_20 ; to next mark and loop
  816.  
  817. @strstk_update_marks_99:
  818.     @restore
  819.     ret
  820. strstk_update_marks endp
  821.  
  822.  
  823.  
  824. ;+
  825. ; FUNCTION : strstk_mark_cur
  826. ;
  827. ;    Changes the topmost mark to point to the current string. If no marks
  828. ;    exist, one is created.
  829. ;
  830. ; Parameters:
  831. ;    BX    := address of descriptor.
  832. ;
  833. ; Returns:
  834. ;    CF = 1 if error (no room), else 0.
  835. ; Register AX is destroyed.
  836. ;-
  837. strstk_mark_cur proc near
  838.     call    strstk_kill_mark
  839.     call    strstk_push_mark
  840.     ret
  841. strstk_mark_cur endp
  842.  
  843.  
  844. ;+
  845. ; FUNCTION : strstk_kill_mark
  846. ;
  847. ;    Deletes the topmost mark on the stack.
  848. ;
  849. ; Parameters:
  850. ;    BX    := address of buffer descriptor
  851. ;
  852. ; Returns:
  853. ;    CF = 1 if stack was empty, else 0.
  854. ; Register AX is destroyed.
  855. ;-
  856. strstk_kill_mark proc near
  857.     mov    ax,[bx].high_end    ;Highest location of buffer
  858.     cmp    ax,[bx].topmark    ;Marker stack empty ?
  859.     jb    @strstk_kill_mark_99 ;Yes
  860.     dec    [bx].topmark    ;Remove topmost mark by updating
  861.     dec    [bx].topmark    ; top of stack ptr
  862. @strstk_kill_mark_99:
  863.     ret
  864. strstk_kill_mark endp
  865.  
  866.  
  867. ;+
  868. ; FUNCTION : strstk_push_mark
  869. ;
  870. ;    Pushes a marker to the current string onto the marker stack.
  871. ;
  872. ; Parameters:
  873. ;    BX    := address of descriptor
  874. ;
  875. ; Returns:
  876. ;    CF = 1 if error (no room), else 0.
  877. ; Register AX is destroyed.
  878. ;-
  879. strstk_push_mark proc near
  880.     @save    di
  881.     mov    di,[bx].topmark    ;Point to topmost mark
  882.     dec    di
  883.     dec    di        ;Next location to push mark
  884.     cmp    di,[bx].top    ;Enough room ?
  885.     jle    @strstk_push_mark_90 ;Sorry, return error
  886.     mov    [bx].topmark,di    ;New stack top
  887.     mov    ax,[bx].cur    ;Address of current string
  888.     stosw            ;Store in top marker
  889.     ;Carry is clear
  890.     jmp    short @strstk_push_mark_99
  891. @strstk_push_mark_90:
  892.     stc            ;Set error
  893. @strstk_push_mark_99:
  894.     @restore
  895. strstk_push_mark endp
  896.  
  897.  
  898. ;+
  899. ; FUNCTION : strstk_pop_mark
  900. ;
  901. ;    Pops the topmost mark off the stack and set the current ptr
  902. ;    to point to its associated string.
  903. ;
  904. ; Parameters:
  905. ;    BX    := address of buffer descriptor
  906. ;
  907. ; Returns:
  908. ;    CF = 1 if no marks, else 0.
  909. ; Register AX is destroyed.
  910. ;-
  911. strstk_pop_mark proc near
  912.     call    strstk_goto_mark
  913.     call    strstk_kill_mark
  914.     ret
  915. strstk_pop_mark endp
  916.  
  917.  
  918.  
  919. ;+
  920. ; FUNCTION : strstk_goto_mark
  921. ;
  922. ;    Sets the current string pointer to the string associated with the
  923. ;    topmost marker on the marker stack.
  924. ;
  925. ; Parameters:
  926. ;    BX    := address of buffer descriptor
  927. ;
  928. ; Returns:
  929. ;    CF = 1 if no markers, else 0.
  930. ; Register AX is destroyed.
  931. ;-
  932. strstk_goto_mark proc near
  933.     mov    ax,[bx].high_end
  934.     cmp    ax,[bx].topmark
  935.     jb    @strstk_goto_mark_99    ;No marks
  936.     mov    ax,[bx].topmark
  937.     mov    [bx].cur,ax        ;Update current string ptr
  938. @strstk_goto_mark_99:
  939.     ret
  940. strstk_goto_mark endp
  941.  
  942.  
  943. ;+
  944. ; FUNCTION : strstk_purge_marks
  945. ;
  946. ;    Deletes all marks that point to the sentinel string. The marker
  947. ;    stack is compacted.
  948. ;
  949. ; Parameters:
  950. ;    BX    := address of buffer descriptor
  951. ;
  952. ; Returns:
  953. ;    Nothing.
  954. ; Register(s)  AX,CX are destroyed.
  955. ;-
  956. strstk_purge_marks proc near
  957.     @save    si,di
  958.     mov    cx,[bx].low_end
  959.     inc    cx        ;CX = sentinel string trailer
  960.     mov    di,[bx].high_end
  961.     inc    di
  962.     mov    si,di
  963. @strstk_purge_marks_10:        ;SI = last marker location examined
  964.                 ;DI = last marker location stored
  965.     cmp    si,[bx].topmark    ;Checked all markers ?
  966.     je    @strstk_purge_marks_99 ;Yes, exit
  967.     dec    si
  968.     dec    si        ;Next location to check
  969.     mov    ax,[si]        ;Marker value
  970.     cmp    ax,cx        ;Points to sentinel ?
  971.     jg    @strstk_purge_marks_10 ;No, go check next
  972.     dec    di
  973.     dec    di        ;DI = next location to store
  974.     mov    [di],ax        ;Store marker
  975.     jmp    short @strstk_purge_marks_10
  976. @strstk_purge_marks_99:
  977.     mov    [bx].topmark,di    ;Store new top of marker stack
  978.     @restore
  979.     ret
  980. strstk_purge_marks endp
  981.  
  982.     ENDIF                ;WANT_MARKERS
  983.  
  984. ;+
  985. ; FUNCTION : strstk_output by wd
  986. ;
  987. ;    Output the entire stack, one item per line.
  988. ;
  989. ; Parameters:
  990. ;    BX  := address of buffer descriptor
  991. ;
  992. ; Returns:
  993. ;    Nothing.
  994. ;-
  995. strstk_output proc near
  996.     @save    si,di
  997.     push    bp
  998.     mov    bp,sp
  999.     sub    sp,LINEBUF_SIZE
  1000. display_buf equ <byte ptr [bp-LINEBUF_SIZE]>
  1001.  
  1002.     call    near ptr strstk_save_cur    ;Save current pointer.
  1003. ;                         Probably not necessary for
  1004. ;                         symbols, but do anyway
  1005.     call    near ptr strstk_setbot        ;Go to bottom of stack
  1006.  
  1007. @strstk_output_10:
  1008. ; Copy the string into the display buffer and display it.
  1009.     lea    ax,display_buf
  1010.     mov    cx,LINEBUF_SIZE
  1011.     call    near ptr strstk_copy
  1012.     test    ax,ax
  1013.     jz    @strstk_output_20
  1014.     push    bx                ;Save BX
  1015.     push    ax
  1016.     call    near ptr output_newline
  1017.     pop    cx                ;CX<-length of string
  1018.     pop    bx
  1019.     push    bx
  1020.     lea    dx,display_buf            ;DX->string, CX is count
  1021.     call    near ptr output_counted_string
  1022.     pop    bx
  1023.  
  1024. @strstk_output_20:
  1025.     xor    cx,cx
  1026.     call    near ptr strstk_fwd_match
  1027.     jnc    @strstk_output_10
  1028.  
  1029. @strstk_output_99:
  1030.     call    near ptr strstk_restore_cur ;Restore the current pointer
  1031.  
  1032.     mov    sp,bp
  1033.     pop    bp
  1034.     @restore
  1035.     ret
  1036. strstk_output endp
  1037.  
  1038.  
  1039. CSEG    ENDS
  1040.  
  1041.     END
  1042.